package myMybatis;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.lang.reflect.*;
import java.sql.*;
import java.util.*;

public class MyInvocationHandler implements InvocationHandler {

    private Object object;

    //为目标创建动态代理对象
    public Object getInstance(Class cls){
        Object newProxyInstance = Proxy.newProxyInstance(
                cls.getClassLoader(),//类加载器
                new Class[]{cls},
                this
        );
        return newProxyInstance;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //1、读取xml，获取连接参数
        Map<String, String> dataSourceProperty = getDataSourceProperty();
        //2、创建数据库连接对象
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass(dataSourceProperty.get("driver"));
        dataSource.setJdbcUrl(dataSourceProperty.get("url"));
        dataSource.setUser(dataSourceProperty.get("username"));
        dataSource.setPassword(dataSourceProperty.get("password"));
        Connection connection = dataSource.getConnection();
        //3、执行SQL，获取结果集。
        //获取方法对应的statement
        Map<String, String> sqlMap = parseStatement(method.getName());
        String sql = sqlMap.get("sql");
        String resultType = sqlMap.get("resultType");
        String parameterType = sqlMap.get("parameterType");
        //4、根据实体类解析结果集  结果集-Java对象
        return createObj(connection,sql,resultType,parameterType,args);
    }


    /**
     * 执行 SQL，映射结果集
     * @param connection
     * @param sql
     * @param resultType
     * @param parameterType
     * @param args
     * @return
     */
    public Object createObj(Connection connection,String sql,String resultType,String parameterType,Object[] args){
        Object result = null;
        try {
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            if (parameterType != null) {
                switch (parameterType) {
                    case "java.lang.Integer":
                        preparedStatement.setInt(1, (Integer) args[0]);
                        break;
                    case "java.lang.String":
                        break;
                }
            }
            ResultSet resultSet = preparedStatement.executeQuery();
            //将结果集映射成Java对象
            try {
                Class aClass = Class.forName(resultType);
                result = parseObject(resultSet, aClass);
            }catch (Exception e){
                e.printStackTrace();
            }

        } catch (Exception throwables) {
            throwables.printStackTrace();
        }
        return result;
    }



    public Object parseObject(ResultSet resultSet,Class aClass) throws SQLException {
        //多行数据，映射成集合
        List list = new ArrayList();
        ResultSetMetaData metaData = resultSet.getMetaData();
        try {
            while (resultSet.next()) {
                String columnName = null;
                String columnTypeName = null;
                Object object = aClass.getConstructor(null).newInstance(null);
                Object value = null;
                for (int i = 1; i <= metaData.getColumnCount(); i++) {
                    columnName = metaData.getColumnName(i);
                    columnTypeName = metaData.getColumnTypeName(i);
                    switch (columnTypeName){
                        case "INT":
                            value = resultSet.getInt(columnName);
                            break;
                        case "VARCHAR":
                            value = resultSet.getString(columnName);
                            break;
                    }
                    //给object赋值
                    //获取方法
                    String methodName = "set"+columnName.substring(0,1).toUpperCase()+columnName.substring(1);
                    //获取实体类属性
                    Field declaredField = aClass.getDeclaredField(columnName);
                    Method method = aClass.getMethod(methodName, declaredField.getType());
                    method.invoke(object,value);
                }
                list.add(object);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if(list.size() == 1) return list.get(0);
        return list;
    }



    /**
     * 解析 xml，获取对应 SQL
     * @param methodName
     * @return
     */
    public Map<String,String> parseStatement(String methodName){
        Map<String, String> maps = new HashMap<>();
        try {
            //通过使用 dom4j 读取 UserMapper.xml
            SAXReader reader = new SAXReader();
            Document document = reader.read("myBatis001/src/main/java/myMyBatis/UserMapper.xml");
            //获取配置文件中的数据
            Element rootElement = document.getRootElement();
            Iterator<Element> iterator = rootElement.elementIterator();
            while (iterator.hasNext()) {
                Element next = iterator.next();
                String id = next.attributeValue("id");
                if (id.equals(methodName)){
                    String sql = next.getText();
//                    System.out.println(sql);
                    maps.put("sql",sql);
                    String parameterType = next.attributeValue("parameterType");
                    String resultType = next.attributeValue("resultType");
                    maps.put("resultType",resultType);
                    maps.put("parameterType",parameterType);
                }
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return maps;
    }


    /**
     * 读取 xml文件，获取数据源。
     * @return
     */
    public Map<String,String> getDataSourceProperty(){
        Map<String, String> maps = new HashMap<>();
        try {
            //通过使用 dom4j 读取 config.xml
            SAXReader reader = new SAXReader();
            Document document = reader.read("myBatis001/src/main/resources/config.xml");
            //获取配置文件中的数据
            Element rootElement = document.getRootElement();
            Iterator<Element> iterator = rootElement.elementIterator();
            while (iterator.hasNext()) {
                Element next = iterator.next();
    //            System.out.println(next.getName());
                if ("environments".equals(next.getName())){
                    Iterator<Element> iterator1 = next.elementIterator();
                    while (iterator1.hasNext()) {
                        Element next1 = iterator1.next();
    //                    System.out.println(next1.getName());
                        Iterator<Element> iterator2 = next1.elementIterator();
                        while (iterator2.hasNext()) {
                            Element next2 = iterator2.next();
    //                        System.out.println(next2.getName());
                            if ("dataSource".equals(next2.getName())) {
    //                            System.out.println(next2.getName());
                                Iterator<Element> iterator3 = next2.elementIterator();
                                while (iterator3.hasNext()) {
                                    Element next3 = iterator3.next();
                                    String name = next3.attributeValue("name");
                                    String value = next3.attributeValue("value");
    //                                System.out.println(name+":"+value);
                                    maps.put(name,value);
                                }
                            }
                        }
                    }
                }
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return maps;
    }

}

